<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="soulwebsocket-bootstrap">
      Soul网关源码解析（十三）Websocket同步数据-Bootstrap端
     </h1>
     <hr/>
     <h2 id="_1">
      简介
     </h2>
     <p>
      此篇文章，我们来探索下Websocket数据同步具体细节
     </p>
     <h2 id="_2">
      示例运行
     </h2>
     <p>
      启动数据库：
     </p>
     <p>
      ```shell script
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">首先运行</span><span class="n">Soul</span><span class="o">-</span><span class="k">Admin</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">配置</span><span class="n">Soul</span><span class="o">-</span><span class="n">Bootstrap</span><span class="err">，配置</span><span class="n">websocket同步方式</span><span class="err">，运行</span><span class="n">Soul</span><span class="o">-</span><span class="n">Bootstrap</span><span class="err">，大致如下：</span>

<span class="o">```</span><span class="n">xml</span>
<span class="n">soul</span> <span class="p">:</span>
    <span class="n">file</span><span class="p">:</span>
      <span class="n">enabled</span><span class="p">:</span> <span class="k">true</span>
    <span class="n">corss</span><span class="p">:</span>
      <span class="n">enabled</span><span class="p">:</span> <span class="k">true</span>
    <span class="n">dubbo</span> <span class="p">:</span>
      <span class="k">parameter</span><span class="p">:</span> <span class="n">multi</span>
    <span class="o">#</span> <span class="err">把</span> <span class="n">websocket</span> <span class="err">数据同步打开</span>
    <span class="n">sync</span><span class="p">:</span>
        <span class="n">websocket</span> <span class="p">:</span>
             <span class="n">urls</span><span class="p">:</span> <span class="n">ws</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">9095</span><span class="o">/</span><span class="n">websocket</span>
</code></pre>
     </div>
     <p>
      运行Soul-Example-HTTP，注册一些数据用于Debug测试
     </p>
     <h2 id="debug">
      源码Debug
     </h2>
     <p>
      在上篇的分析中，我们知道了数据更新的核心类及其核心代码如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CommonPluginDataSubscriber</span> <span class="kd">implements</span> <span class="n">PluginDataSubscriber</span> <span class="p">{</span>
    <span class="p">.......</span>

    <span class="c1">// 下面这类函数又进行了一次封装，我们可以看到这里有那么一点小瑕疵，命名有点不统一；onSubscribe这个函数取的不是太好，不能见名识意，有点宽泛了</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">PluginData</span> <span class="n">pluginData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">pluginData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">unSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">PluginData</span> <span class="n">pluginData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">pluginData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onSelectorSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selectorData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">selectorData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">unSelectorSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selectorData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">selectorData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onRuleSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">RuleData</span> <span class="n">ruleData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">ruleData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">unRuleSubscribe</span><span class="p">(</span><span class="kd">final</span> <span class="n">RuleData</span> <span class="n">ruleData</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subscribeDataHandler</span><span class="p">(</span><span class="n">ruleData</span><span class="p">,</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="nf">subscribeDataHandler</span><span class="p">(</span><span class="kd">final</span> <span class="n">T</span> <span class="n">classData</span><span class="p">,</span> <span class="kd">final</span> <span class="n">DataEventTypeEnum</span> <span class="n">dataType</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">classData</span><span class="p">).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">data</span> <span class="o">-&gt;</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">data</span> <span class="k">instanceof</span> <span class="n">PluginData</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">PluginData</span> <span class="n">pluginData</span> <span class="o">=</span> <span class="p">(</span><span class="n">PluginData</span><span class="p">)</span> <span class="n">data</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 插件配置数据的更新</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">cachePluginData</span><span class="p">(</span><span class="n">pluginData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">pluginData</span><span class="p">.</span><span class="na">getName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">handlerPlugin</span><span class="p">(</span><span class="n">pluginData</span><span class="p">));</span>
                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 插件配置数据的删除</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">removePluginData</span><span class="p">(</span><span class="n">pluginData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">pluginData</span><span class="p">.</span><span class="na">getName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">removePlugin</span><span class="p">(</span><span class="n">pluginData</span><span class="p">));</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">data</span> <span class="k">instanceof</span> <span class="n">SelectorData</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">SelectorData</span> <span class="n">selectorData</span> <span class="o">=</span> <span class="p">(</span><span class="n">SelectorData</span><span class="p">)</span> <span class="n">data</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 选择器数据的更新</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">cacheSelectData</span><span class="p">(</span><span class="n">selectorData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">selectorData</span><span class="p">.</span><span class="na">getPluginName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">handlerSelector</span><span class="p">(</span><span class="n">selectorData</span><span class="p">));</span>
                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 选择器数据的删除</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">removeSelectData</span><span class="p">(</span><span class="n">selectorData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">selectorData</span><span class="p">.</span><span class="na">getPluginName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">removeSelector</span><span class="p">(</span><span class="n">selectorData</span><span class="p">));</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">data</span> <span class="k">instanceof</span> <span class="n">RuleData</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">RuleData</span> <span class="n">ruleData</span> <span class="o">=</span> <span class="p">(</span><span class="n">RuleData</span><span class="p">)</span> <span class="n">data</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">UPDATE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 规则数据的更新</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">cacheRuleData</span><span class="p">(</span><span class="n">ruleData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">ruleData</span><span class="p">.</span><span class="na">getPluginName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">handlerRule</span><span class="p">(</span><span class="n">ruleData</span><span class="p">));</span>
                <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">dataType</span> <span class="o">==</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">DELETE</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// 规则数据的删除</span>
                    <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">removeRuleData</span><span class="p">(</span><span class="n">ruleData</span><span class="p">);</span>
                    <span class="n">Optional</span><span class="p">.</span><span class="na">ofNullable</span><span class="p">(</span><span class="n">handlerMap</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">ruleData</span><span class="p">.</span><span class="na">getPluginName</span><span class="p">())).</span><span class="na">ifPresent</span><span class="p">(</span><span class="n">handler</span> <span class="o">-&gt;</span> <span class="n">handler</span><span class="p">.</span><span class="na">removeRule</span><span class="p">(</span><span class="n">ruleData</span><span class="p">));</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      我们先拉看一手插件配置数据，在上面的：onSubscribe 和 unSubscribe 打上断点，重启Soul-Bootstrap
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PluginDataHandler</span> <span class="kd">extends</span> <span class="n">AbstractDataHandler</span><span class="o">&lt;</span><span class="n">PluginData</span><span class="o">&gt;</span> <span class="p">{</span>

    <span class="c1">// 恢复，启动的时候执行</span>
    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doRefresh</span><span class="p">(</span><span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">PluginData</span><span class="o">&gt;</span> <span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// 清空缓存数据</span>
        <span class="n">pluginDataSubscriber</span><span class="p">.</span><span class="na">refreshPluginDataSelf</span><span class="p">(</span><span class="n">dataList</span><span class="p">);</span>
        <span class="n">dataList</span><span class="p">.</span><span class="na">forEach</span><span class="p">(</span><span class="n">pluginDataSubscriber</span><span class="p">::</span><span class="n">onSubscribe</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// 数据更新（增改）</span>
    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doUpdate</span><span class="p">(</span><span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">PluginData</span><span class="o">&gt;</span> <span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">dataList</span><span class="p">.</span><span class="na">forEach</span><span class="p">(</span><span class="n">pluginDataSubscriber</span><span class="p">::</span><span class="n">onSubscribe</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// 数据删除</span>
    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">doDelete</span><span class="p">(</span><span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">PluginData</span><span class="o">&gt;</span> <span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">dataList</span><span class="p">.</span><span class="na">forEach</span><span class="p">(</span><span class="n">pluginDataSubscriber</span><span class="p">::</span><span class="n">unSubscribe</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      这个类就是websocket同步模块中唯一调用CommonPluginDataSubscriber相关接口的，可以看到它就三个接口：恢复、更新、删除
     </p>
     <p>
      datalist我们查看值，可以发现全是我们插件相关的数据，很重要，我们继续跟踪其来源
     </p>
     <p>
      我们接着查看调用栈，来到下面的函数，看到它对应调用了前面函数的三个接口
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">AbstractDataHandler</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="kd">implements</span> <span class="n">DataHandler</span> <span class="p">{</span>

    <span class="c1">// 对应上面的三个操作：恢复、更新、删除</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">handle</span><span class="p">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">json</span><span class="p">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">eventType</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">List</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">dataList</span> <span class="o">=</span> <span class="n">convert</span><span class="p">(</span><span class="n">json</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">CollectionUtils</span><span class="p">.</span><span class="na">isNotEmpty</span><span class="p">(</span><span class="n">dataList</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">DataEventTypeEnum</span> <span class="n">eventTypeEnum</span> <span class="o">=</span> <span class="n">DataEventTypeEnum</span><span class="p">.</span><span class="na">acquireByName</span><span class="p">(</span><span class="n">eventType</span><span class="p">);</span>
            <span class="k">switch</span> <span class="p">(</span><span class="n">eventTypeEnum</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">case</span> <span class="n">REFRESH</span><span class="p">:</span>
                <span class="k">case</span> <span class="n">MYSELF</span><span class="p">:</span>
                    <span class="n">doRefresh</span><span class="p">(</span><span class="n">dataList</span><span class="p">);</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="k">case</span> <span class="n">UPDATE</span><span class="p">:</span>
                <span class="k">case</span> <span class="n">CREATE</span><span class="p">:</span>
                    <span class="n">doUpdate</span><span class="p">(</span><span class="n">dataList</span><span class="p">);</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="k">case</span> <span class="n">DELETE</span><span class="p">:</span>
                    <span class="n">doDelete</span><span class="p">(</span><span class="n">dataList</span><span class="p">);</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="k">default</span><span class="p">:</span>
                    <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      我们查看下DataEventTypeEnum:DELETE/CREATE/UPDATE/REFRESH/MYSELF,基本能对应上下面的SWITH的case
     </p>
     <p>
      而且看到json转成的dataList，我们继续跟
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">WebsocketDataHandler</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">executor</span><span class="p">(</span><span class="kd">final</span> <span class="n">ConfigGroupEnum</span> <span class="n">type</span><span class="p">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">json</span><span class="p">,</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">eventType</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">ENUM_MAP</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">type</span><span class="p">).</span><span class="na">handle</span><span class="p">(</span><span class="n">json</span><span class="p">,</span> <span class="n">eventType</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      这个函数type，和evenTtype稍微注意下，看一看ConfigGroupEnum:APP_AUTH/PLUGIN/RULE/SELECTOR/META_DATA,找了一些前面我们忽略的同步数据，发现除了插件配置数据、选择器数据、规则数据，还有APP_AUTH数据和META_DATA（感觉和RPC相关），后面我们补上测一测
     </p>
     <p>
      继续跟，来到属性的Websocket，和前面分析文章中的基本一致，都是通过起一个websocket客户端，接收到消息后触发调用
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="nd">@Slf4j</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">SoulWebsocketClient</span> <span class="kd">extends</span> <span class="n">WebSocketClient</span> <span class="p">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onMessage</span><span class="p">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">result</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">handleResult</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nd">@SuppressWarnings</span><span class="p">(</span><span class="s">"ALL"</span><span class="p">)</span>
    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">handleResult</span><span class="p">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">result</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">WebsocketData</span> <span class="n">websocketData</span> <span class="o">=</span> <span class="n">GsonUtils</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">fromJson</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">WebsocketData</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
        <span class="n">ConfigGroupEnum</span> <span class="n">groupEnum</span> <span class="o">=</span> <span class="n">ConfigGroupEnum</span><span class="p">.</span><span class="na">acquireByName</span><span class="p">(</span><span class="n">websocketData</span><span class="p">.</span><span class="na">getGroupType</span><span class="p">());</span>
        <span class="n">String</span> <span class="n">eventType</span> <span class="o">=</span> <span class="n">websocketData</span><span class="p">.</span><span class="na">getEventType</span><span class="p">();</span>
        <span class="n">String</span> <span class="n">json</span> <span class="o">=</span> <span class="n">GsonUtils</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">toJson</span><span class="p">(</span><span class="n">websocketData</span><span class="p">.</span><span class="na">getData</span><span class="p">());</span>
        <span class="n">websocketDataHandler</span><span class="p">.</span><span class="na">executor</span><span class="p">(</span><span class="n">groupEnum</span><span class="p">,</span> <span class="n">json</span><span class="p">,</span> <span class="n">eventType</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      基本上一个初始化的流程是更新完毕了
     </p>
     <p>
      后面我们测试下修改，在后面管理界面，把rate_limiter状态给关了，修改后立马进入了debug，收到的数据如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="p">{</span><span class="nt">"groupType"</span><span class="p">:</span><span class="s2">"PLUGIN"</span><span class="p">,</span><span class="nt">"eventType"</span><span class="p">:</span><span class="s2">"UPDATE"</span><span class="p">,</span><span class="nt">"data"</span><span class="p">:[{</span><span class="nt">"id"</span><span class="p">:</span><span class="s2">"4"</span><span class="p">,</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"rate_limiter"</span><span class="p">,</span><span class="nt">"config"</span><span class="p">:</span><span class="s2">"{\"mode\":\"standalone\",\"master\":\"mymaster\",\"url\":\"127.0.0.1:6379\"}"</span><span class="p">,</span><span class="nt">"role"</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="nt">"enabled"</span><span class="p">:</span><span class="kc">false</span><span class="p">}]}</span>
</code></pre>
     </div>
     <p>
      可以看到比较关键的数据：groupType、eventType、data等等
     </p>
     <p>
      后面进行了删除的测试，流程基本一致，类型变了而已
     </p>
     <p>
      后面进行其他类型的测试：PLUGIN/RULE/SELECTOR/META_DATA,APP_AUTH不太去确定，就没有测
     </p>
     <p>
      总体而言还是比较简单清晰，那就直接总结一波
     </p>
     <h2 id="_3">
      总结
     </h2>
     <p>
      总体来说Websocket同步还是比较简单的,可能让人疑惑的是Websocket的使用吧。由于在工作中还是比较常使用websocket，所以理解读数据那块还是比较轻松的，如果大家有疑惑的话，可以搜索Spring Websocket的使用教程，自己动手玩一玩，估计就可以了
     </p>
     <p>
      总结今天的Websocket同步如下图：
     </p>
     <p>
      <img alt="" src="./picture/websocket-bootstrap.png"/>
     </p>
     <p>
      如上图所示，数据更新的流程如下：
     </p>
     <ul>
      <li>
       1.SoulWebsocketClient ：从Soul-Admin接收数据，进行更新，里面有数据类型、操作类型还有具体信息
      </li>
      <li>
       2.WebsocketDataHandler ：根据这ConfigGroupEnum（数据类型），调用相应的subscribe
      </li>
      <li>
       3.subscribe ：具体subscribe调用相应的类型的DataCache进行数据更新和删除
      </li>
     </ul>
     <h2 id="soul">
      Soul网关源码分析文章列表
     </h2>
     <h3 id="github">
      Github
     </h3>
     <ul>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB1-%E6%A6%82%E8%A7%88.md">
        Soul源码阅读（一） 概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB2-%E5%88%9D%E6%AD%A5%E8%BF%90%E8%A1%8C.md">
        Soul源码阅读（二）代码初步运行
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB3-%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
        Soul源码阅读（三）HTTP请求处理概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB4-dubbo%E8%AF%B7%E6%B1%82%E6%A6%82%E8%A7%88.md">
        Soul网关源码阅读（四）Dubbo请求概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB5-%E8%AF%B7%E6%B1%82%E7%B1%BB%E5%9E%8B%E6%8E%A2%E7%B4%A2.md">
        Soul网关源码阅读（五）请求类型探索
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB6-sofa%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
        Soul网关源码阅读（六）Sofa请求处理概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB7-%E9%99%90%E6%B5%81%E6%8F%92%E4%BB%B6%E5%88%9D%E6%8E%A2.md">
        Soul网关源码阅读（七）限流插件初探
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/0e6931519a84d5c603504b2c6a633698ac793b70/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB8-%E8%B7%AF%E7%94%B1%E5%8C%B9%E9%85%8D%E5%88%9D%E6%8E%A2.md">
        Soul网关源码阅读（八）路由匹配初探
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB9-%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E5%8A%A0%E8%BD%BD%E5%88%9D%E6%8E%A2.md">
        Soul网关源码阅读（九）插件配置加载初探
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB10-%E8%87%AA%E5%AE%9A%E4%B9%89%E7%AE%80%E5%8D%95%E6%8F%92%E4%BB%B6%E7%BC%96%E5%86%99.md">
        Soul网关源码阅读（十）自定义简单插件编写
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB11-%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E5%B0%8F%E7%BB%93.md">
        Soul网关源码阅读（十一）请求处理小结
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB12-%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5%E5%88%9D%E6%8E%A2.md">
        Soul网关源码阅读（十二）数据同步初探-Bootstrap端
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB13-websocket%E5%90%8C%E6%AD%A5%E6%95%B0%E6%8D%AE-Bootstrap%E7%AB%AF.md">
        Soul网关源码阅读（十三）Websocket同步数据-Bootstrap端
       </a>
      </li>
      <li>
       <p>
        <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB14-HTTP%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5-Bootstrap%E7%AB%AF.md">
         Soul网关源码阅读（十四）HTTP数据同步-Bootstrap端
        </a>
       </p>
      </li>
      <li>
       <p>
        <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%95%AA%E5%A4%96%E7%AF%871-HTTP%E7%A4%BA%E4%BE%8B%E5%8F%82%E6%95%B0%E8%AF%B7%E6%B1%82%E9%94%99%E8%AF%AF.md">
         Soul网关源码阅读番外篇（一） HTTP参数请求错误
        </a>
       </p>
      </li>
     </ul>
     <h3 id="_4">
      掘金
     </h3>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6917864624423436296">
        Soul网关源码阅读（一） 概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917865804121767944">
        Soul网关源码阅读（二）代码初步运行
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917866538712334343">
        Soul网关源码阅读（三）请求处理概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917867369909977102">
        Soul网关源码阅读（四）Dubbo请求概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6918575905962983438">
        Soul网关源码阅读（五）请求类型探索
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6918736260467015693">
        Soul网关源码阅读（六）Sofa请求处理概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6919348164944232455/">
        Soul网关源码阅读（七）限流插件初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6919774553241550855/">
        Soul网关源码阅读（八）路由匹配初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920074307590684685/">
        Soul网关源码阅读（九）插件配置加载初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920142348617777166">
        Soul网关源码阅读（十）自定义简单插件编写
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920596034171174925">
        Soul网关源码阅读（十一）请求处理小结
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920596173925384206">
        Soul网关源码阅读（十二）数据同步初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920596028505178125">
        Soul网关源码阅读（十三）Websocket同步数据-Bootstrap端
       </a>
      </li>
      <li>
       <p>
        <a href="https://juejin.cn/post/6920597298674302983">
         Soul网关源码阅读（十四）HTTP数据同步-Bootstrap端
        </a>
       </p>
      </li>
      <li>
       <p>
        <a href="https://juejin.cn/post/6918947689564471309">
         Soul网关源码阅读番外篇（一） HTTP参数请求错误
        </a>
       </p>
      </li>
     </ul>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
